package com.it.service;

import com.it.dao.StudentDao;
import com.it.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.FileInputStream;
import java.util.List;

// TODO Spring (3)、声明式事务的使用
/*
 *
 * 【说明】：@Transactional 添加事务
 *         在需要添加事务的类或方法上添加 @Transactional 就表示该类或者该方法添加了事务
 *
 * 【位置】：添加在类上：表示该类下的所有方法都添加上了事务
 *         添加在方法上：表示该方法添加了事务
 *
 * 【事务属性】：
 *         1、只读模式：@Transactional(readOnly = true)
 *         2、超时时间：@Transactional(timeout = 3)
 *         3、事务异常处理：
 *            出现异常回滚：@Transactional(rollbackFor = 指定的异常类型.class)
 *            出现异常不回滚：@Transactional(noRollbackFor = 指定的异常类型.class)
 *         4、事务隔离级别：仅了解、一般不用
 *         5、事务传播行为： @Transactional(propagation = Propagation.MANDATORY)
 *                        @Transactional(propagation = Propagation.REQUIRES_NEW)
 *

 * */
@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;
    /*
     * 只读模式
     * 【说明】：只读模式可以提升查询事务的效率！如果事务中只要查询代码、使用只读模式
     * 【语法】：readOnly=true：表示只读模式、只能查询、如果使用增删改会报错
     *         readOnly=false：表示非只读模式
     * */
    @Transactional(readOnly = true)
    public void readOnly() {
        List<Student> students = studentDao.selectAllBy();
        for (Student i : students) {
            System.out.println(i);
        }
    }

    /*
     * 超时时间
     * 【说明】：设置一个时间期限，超出这个时间、无论成功与否都不在等待，并抛出一个异常
     *          超时回滚、释放资源
     * 【语法】：@Transactional(timeout = 3)；表示最长等待时间为3秒
     *         默认值：-1，表示永远不超时
     *         设置值单位：秒
     * 【注意】：如果在方法上的添加超时时间属性，会覆盖类上的超时时间属性
     *         如果未在方法上添加超时时间，会继承类的超时时间
     * */
    @Transactional(timeout = 3)
    public void exceedTime() {
        try {
            Thread.sleep(4000);                   // 测试超时时间
            studentDao.updateNameById("hhhhhh", 1);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }

    /*
     * 事务异常处理
     * 【说明】：在事务异常处理中，可以设置出现异常回滚和出现异常不回滚
     *         默认情况下、指定发行运行时异常事务才会回滚
     *         我们可以指定 Exception 异常来控制所以异常回滚
     * 【语法】：出现异常回滚：rollbackFor = 异常类型.class
     *         出现异常不回滚：noRollbackFor = 异常类型.class
     * */
    @Transactional(rollbackFor = Exception.class)
    // @Transactional(noRollbackFor = Exception.class)
    public void Exceptions() {
        try {
            new FileInputStream("F:\\123.txt");         // 测试编译时异常
            // int a = 10 / 0;                                 // 测试运行时异常
            studentDao.updateNameById("刘老根", 1);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /*
     * 事务隔离级别
     * 【说明】：基本不会使用，仅作为了解即可
     * 【语法】：@Transactional(isolation = Isolation.DEFAULT)
     *               Isolation.DEFAULT              默认
     *               Isolation.READ_COMMITTED       读已提交
     *               Isolation.SERIALIZABLE         序列化
     *               Isolation.READ_UNCOMMITTED     读未提交
     *               Isolation.REPEATABLE_READ      可重复读
     * */


    /*
     * 事务传播行为
     * 【说明】：添加上事务的方法之间调用，当A方法调用了B、当A出现了问题或异常、会不会影响B
     * 【语法】：
     *         Propagation.MANDATORY（默认值）：如果父方法有事务，就加入，如果没有就自己创建
     *         Propagation.REQUIRES_NEW：不管父方法是否有事务，我都新建事务，是独立的
     * */

    @Transactional(rollbackFor = Exception.class)
    public void spread() {
        this.spreadSon1(); // Propagation.REQUIRES_NEW
        this.spreadSon2(); // Propagation.MANDATORY
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void spreadSon1() {
        // 修改名称
        studentDao.updateNameById("刘老根", 1);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void spreadSon2() {
        // 修改年龄
        studentDao.updateAgeById(123, 1);
//        int i= 1/0; // 报错
    }
}
